﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using CoreEx.Database.Metadata;
using LinFu.IoC.Configuration;
using LinFu.IoC.Interfaces;
using LinFu.IoC;
using CoreEx.Common.Extensions;
namespace CoreEx.Database.MetaData.Implementation
{
    [Implements(typeof(IModelGenerator))]
    public class ModelGenerator : IModelGenerator, IInitialize
    {
        private IServiceContainer _serviceContainer;
        
        public string Namespace { get; set;}
        

        public string OutputDirectory { get; set;}
        

        public IEnumerable<string> Generate(string repositoryName)
        {
            return Generate(repositoryName, ModelGenerationFlags.Contract & ModelGenerationFlags.Implementation);
        }

        public IEnumerable<string> Generate(string repositoryName, ModelGenerationFlags modelGenerationFlags)
        {
            IList<string> files = new List<string>();
            var repository = _serviceContainer.GetService<IMetadataRepository>(repositoryName,false);
            var interfaceGenerator = _serviceContainer.GetService<ITypeGenerator>("InterfaceGenerator");
            var classGenerator = _serviceContainer.GetService<ITypeGenerator>("ClassGenerator");
            interfaceGenerator.Namespace = Namespace;
            classGenerator.Namespace = Namespace;
            foreach (var tableInfo in repository.Tables)
            {
                if (tableInfo.PrimaryKey == null)
                {
                    files.Add(
                        string.Format(
                            "Unable to generate class for table '{0}'. The table does not define a primary key.",
                            tableInfo));
                    continue;
                }

                if ((modelGenerationFlags & ModelGenerationFlags.Contract) == ModelGenerationFlags.Contract)
                {
                    var definition = interfaceGenerator.Generate(tableInfo);
                    files.Add(Save(string.Format("I{0}", tableInfo.TableName.ToSingular()), definition));    
                }
                if ((modelGenerationFlags & ModelGenerationFlags.Implementation) == ModelGenerationFlags.Implementation)
                {
                    var definition = classGenerator.Generate(tableInfo);
                    files.Add(Save(string.Format("{0}", tableInfo.TableName.ToSingular()), definition));
                }                
            }
            return files;
        }


        protected virtual string Save(string name, string typeDefinition)
        {
            if (string.IsNullOrEmpty(OutputDirectory))
                return string.Format("{0}.cs", name);
            
            string path = Path.Combine(OutputDirectory, string.Format("{0}.cs",name));
            if (!Directory.Exists(OutputDirectory))            
                Directory.CreateDirectory(OutputDirectory);


            

            if (File.Exists(path))
            {
                FileInfo fileInfo = new FileInfo(path);
                if (fileInfo.IsReadOnly)
                    return string.Format("Operation aborted. The file {0} is read only. Make sure that the file checked out if regeneration was intended.", path);
            }
                


            var fs = new FileStream(path, FileMode.Create);

            var s = new StreamWriter(fs);
            s.Write(typeDefinition);
            s.Flush();
            s.Close();

            return path;
        }


        public void Initialize(IServiceContainer source)
        {
            _serviceContainer = source;
        }
    }
}
